#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>          /* See NOTES */
#include<sys/socket.h>
#include<strings.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<unistd.h>
#include<sqlite3.h>
#include<signal.h>
#include<string.h>
#include<time.h>


#define N 32  //在这里定义最大的name为32字节，用户名不能超过32字节

#define R 1   //定义user->register
#define L 2   //定义user->login
#define Q 3   //定义user->query查询
#define H 4   //定义user->history查询历史记录



//定义通信双方的信息结构体
typedef struct{
    int type;
    char name[N];
    char data[256]; //传输的数据最大字节数为256字节
}MSG;  //其中的data数组在我们的项目当中可以用于存储密码和需要传递的信息


#define DATABASE "my.db"

int do_searchword(int acceptfd,MSG *msg,char word[]);
int get_date(char *date);
int history_callback(void *arg,int f_num,char** f_value,char** f_name);
int do_client(int acceptfd,sqlite3 *db);
void do_register(int acceptfd,MSG *msg,sqlite3 *db);
int do_login(int acceptfd,MSG *msg,sqlite3 *db);
int do_query(int acceptfd,MSG *msg,sqlite3 *db);
int do_history(int acceptfd,MSG *msg,sqlite3 *db);





int main(int argc,const char *argv[]){

    int sockfd;  //创建socket套接字的文件描述符
    struct sockaddr_in serveraddr;
    int n;   //从终端当中读出数据
    MSG msg;
    sqlite3 *db;  //定义操作数据库的句柄
    int acceptfd;
    pid_t pid;  //用于接收父子进程的id号

    if(argc!=3){
        printf("错误： %s 未传入正确的ip地址和端口号，正确的命令行格式举例:./client 192.168.3.196 1000\n",argv[0]);
        return -1; //传回参数错误标志
    }

    if(sqlite3_open(DATABASE,&db)!=SQLITE_OK){
        printf("%s\n",sqlite3_errmsg(db));
        return -1;
    }else{
        printf("DATABASES OPEN SUCCESS\n");
    }

    if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0){
        perror("socket过程发生错误");
        return -1; //main函数运行终止
    }

    bzero(&serveraddr,sizeof(serveraddr));  //在这里表示清空这个serveraddr的地址空间

    serveraddr.sin_family=AF_INET;
    serveraddr.sin_addr.s_addr=inet_addr(argv[1]);
    serveraddr.sin_port=htons(atoi(argv[2]));  //atio在这里表示将字符串转化为整形变量

    if(bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr))<0){
        perror("fail to bind");
        return -1;
    }

    //将套接字设为监听模式
    if(listen(sockfd,5)<0){  //规定最多监听客户端数目5个
        printf("fail to listen\n");
        return -1;
    }
    //处理僵尸进程
    signal(SIGCHLD,SIG_IGN);


    while(1){
       if((acceptfd=accept(sockfd,NULL,NULL))<0){
           perror("fail to accept");
           return -1;
       }

       if((pid=fork())<0){
           perror("fail to fork");
           return -1;
       }else if(pid==0){  //儿子进程
           //处理客户端具体的事情
           close(sockfd);
           do_client(acceptfd,db);
       }else{  //父亲进程,用来接受客户端的请求
           close(acceptfd);
       }

    }

    return 0;
}


int do_client(int acceptfd,sqlite3 *db){
    MSG msg;
    while(recv(acceptfd,&msg,sizeof(MSG),0)>0){  //>0表示接收成功
        printf("type:%d\n",msg.type);
        switch(msg.type){
            case R:
                do_register(acceptfd,&msg,db);
                break;
            case L:
                do_login(acceptfd,&msg,db);
                break;
            case Q:
                do_query(acceptfd,&msg,db);
                break;
            case H:
                do_history(acceptfd,&msg,db);
                break;
            default:
             printf("Invalid data msg.\n");
        }
    }
    //如过是在while()当中说明客户端没有失败，如果跳出说明客户端已经退出了
    //当客户端停止的时候就会跳出while循环了
    printf("client exit.\n");
    close(acceptfd);  //子进程在结束的时候需要关闭
    exit(0);  //exit(0)一般表示正常退出，在main函数当中,exit的作用和return的作用很相似

    return 0;
}




void do_register(int acceptfd,MSG *msg,sqlite3 *db){
    char *errmsg;
    char sql[128];

    sprintf(sql,"insert into usr values('%s',%s);",msg->name,msg->data);
    printf("%s\n",sql);


    if(sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK){
        printf("%s\n",errmsg);
        strcpy(msg->data, "user name already exist.");
    }else{
        printf("client register ok!\n");
        strcpy(msg->data,"register ok!");
    }


    if(send(acceptfd,msg,sizeof(MSG),0)<0){
        perror("fail to send!!!");
        return;
    }
    return ;
}


int do_login(int acceptfd,MSG *msg,sqlite3 *db){
    char sql[128]={};
    char *errmsg;
    int nrow;
    int ncloumn;
    char **resultp;  //传出函数，查询传输的结果

    sprintf(sql,"select * from usr where name = '%s' and pass = '%s';",msg->name,msg->data);
    printf("%s\n",sql);

    if(sqlite3_get_table(db,sql,&resultp,&nrow,&ncloumn,&errmsg)!=SQLITE_OK){
        printf("%s\n",errmsg);
        return -1;
    }else{
        printf("get table ok!!!\n");
    }
    
    if(nrow==1){  //查询成功
        strcpy(msg->data,"OK");
        send(acceptfd,msg,sizeof(MSG),0);
        return 1;
    }
    
    if(nrow==0){
        strcpy(msg->data,"usr/passwd wrong");
        send(acceptfd,msg,sizeof(MSG),0);
    }
    return 0;
}

//用于查询单词的函数
int do_searchword(int acceptfd,MSG *msg,char word[]){
    FILE *fp;
    int len=0;
    char temp[512]={};
    int result;
    char *p;

   //打开文件，读取文件，进行比对
   if((fp=fopen("dict.txt","r"))==NULL){//只读,检测是否打开文件成功
        perror("fail to open file\n");
        strcpy(msg->data,"Failed to open dict.txt");
        send(acceptfd,msg,sizeof(MSG),0);
        return -1;
   }
    len=strlen(word);
    //打印出要查询的单词
    printf("%s ,len=%d\n",word,len);

    //读文件，来查询单词，fgets()函数读取到一行当中的换行符就停止读取了，当读取到文章末尾EOF的时候就会停下来
    while(fgets(temp,512,fp)!=NULL){  //fgets()表示每次都读一行
        result=strncmp(temp,word,len);
        if(result<0){   //查询的单词比从文件当中读取到的大
            continue;
        }

        if(result>0||((result==0)&&(temp[len]!=' '))){
            break;
        }
        //表示找到了要查询的单词
        p=temp+len;  //指针指向了单词的末尾,数组的temp表示数组的初始地址
        printf("found word:%s\n",p);
        while(*p==' '){
            p++;
        }

        //找到了单词的解释，跳过了所有的空格了
        strcpy(msg->data,p);
        fclose(fp);
        return 1;
    }
    fclose(fp);
    return 0;
   
}

int get_date(char *date){
    time_t t;
    time(&t);
    struct tm *tp;

    //进行时间的转换
    tp=localtime(&t);

    sprintf(date,"%d-%d-%d %d:%d:%d",tp->tm_year+1900,tp->tm_mon+1,tp->tm_mday,tp->tm_hour,tp->tm_min,tp->tm_sec);
    return 0;
}

int do_query(int acceptfd,MSG *msg,sqlite3 *db){
    char date[128];
    char word[64];
    int found=0;  //定义标志检测是否查询到了
    char sql[128];
    char *errmsg;
    //拿出msg结构体中，要查询的单词
    strcpy(word,msg->data);
    found=do_searchword(acceptfd,msg,word);

    printf("查询一个单词完毕\n");

    //表示已经查询到了单词，那么应该将用户名，时间，单词，插入到历史记录当中
    if(found==1){
        //需要获取系统时间
        get_date(date);
        sprintf(sql,"insert into record values('%s','%s','%s')",msg->name,date,word);
        printf("%s\n",sql);

        if(sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK){
            printf("%s\n",errmsg);
            return -1;
        }


    }else{    //表示没有找到
        strcpy(msg->data,"Not found!!!");
    }

    //将查询到的结果发送回去
    send(acceptfd,msg,sizeof(MSG),0);

    return 0;
}

//得到查询结果，并且将历史记录发送给客户端
int history_callback(void *arg,int f_num,char** f_value,char** f_name){
    int acceptfd;

    MSG msg;


    acceptfd=*((int *)arg);
    sprintf(msg.data,"%s,%s",f_value[1],f_value[2]);
    send(acceptfd,&msg,sizeof(MSG),0);
    return 0;
}


int do_history(int acceptfd,MSG *msg,sqlite3 *db){
    char sql[128]={};
    char *errmsg;
    sprintf(sql,"select * from record where name = '%s'",msg->name);

    //查询数据库
    if(sqlite3_exec(db,sql,history_callback,(void *)&acceptfd,&errmsg)!=SQLITE_OK){
        printf("%s\n",errmsg);
    }else{
        printf("Query record done.\n");
    }

    //查询完毕之后，给客户端发送一个结束信号
    msg->data[0]='\0';
    send(acceptfd,msg,sizeof(MSG),0);
    return 0;
}

